package model;

import java.util.*;

import database.*;

public class SelectQuery {
	private final String selectQuery;
	private final List<SelectQueryData> selectQueryDatas;
	public SelectQuery(String selectQuery){
		this.selectQuery=selectQuery.toLowerCase();
		selectQueryDatas=new ArrayList<SelectQueryData>();
	}
	public List<SelectQueryData> queryParse(List<Row> rows,List<Field> fields){
		selectQueryDatas.clear();
		
		String regex="select ";
        List<String> items=Utilities.SplitByRegex(selectQuery,regex);
        if(items.size()==1){
        	if(!CheckSelectQuery(items.get(0))){
        		return null;
        	}
        	String input=items.get(0);
        	List<Integer> commaPosition=GetCommaPosition(input);
        	List<String> parts=SplitByPosition(input,commaPosition);
        	for(int i=0;i<parts.size();i++){
        		if(parts.get(i).equalsIgnoreCase("*")){
        			if(parts.size()!=1){
        				return null;
        			}
        			handleAll(rows); 
        			return selectQueryDatas;
        		}else if(parts.get(i).indexOf("distinct")>=0){
        			if(parts.size()!=1){
        				return null;
        			}
        			if(!handleDistinct(parts.get(i),rows,fields)){
        				return null;
        			}
        		}else if(parts.get(i).indexOf("sum")>=0){
        			String temp=parts.get(i).replace("sum", "").trim();
        			SelectQueryData data=new SelectQueryData();
        			List<String> columnNames=handleSumMinMaxAvg(temp);
        			if(columnNames==null){
        				return null;
        			}
        			data.setColumnNames(columnNames);
        			data.setSelectQueryType(SelectQueryType.SUM);
        			selectQueryDatas.add(data);
        		}else if(parts.get(i).indexOf("min")>=0){
        			String temp=parts.get(i).replace("min", "").trim();
        			SelectQueryData data=new SelectQueryData();
        			List<String> columnNames=handleSumMinMaxAvg(temp);
        			if(columnNames==null){
        				return null;
        			}
        			data.setColumnNames(columnNames);
        			data.setSelectQueryType(SelectQueryType.MIN);
        			selectQueryDatas.add(data);
        		}else if(parts.get(i).indexOf("max")>=0){
        			String temp=parts.get(i).replace("max", "").trim();
        			SelectQueryData data=new SelectQueryData();
        			List<String> columnNames=handleSumMinMaxAvg(temp);
        			if(columnNames==null){
        				return null;
        			}
        			data.setColumnNames(columnNames);
        			data.setSelectQueryType(SelectQueryType.MAX);
        			selectQueryDatas.add(data);
        		}else if(parts.get(i).indexOf("avg")>=0){
        			String temp=parts.get(i).replace("avg", "").trim();
        			SelectQueryData data=new SelectQueryData();
        			List<String> columnNames=handleSumMinMaxAvg(temp);
        			if(columnNames==null){
        				return null;
        			}
        			data.setColumnNames(columnNames);
					data.setSelectQueryType(SelectQueryType.AVG);
					selectQueryDatas.add(data);
				} else if (parts.get(i).indexOf("count") >= 0) {
					String temp=parts.get(i).replace("count", "").trim();
        			SelectQueryData data=new SelectQueryData();
        			List<String> columnNames=handleSumMinMaxAvg(temp);
        			if(columnNames==null){
        				return null;
        			}
        			data.setColumnNames(columnNames);
					data.setSelectQueryType(SelectQueryType.COUNT);
					selectQueryDatas.add(data);
				} else {
					SelectQueryData data = new SelectQueryData();
					List<String> columnNames = new ArrayList<String>();
					columnNames.add(parts.get(i));
					data.setColumnNames(columnNames);
					data.setSelectQueryType(SelectQueryType.NORMAL);
					selectQueryDatas.add(data);
				}
        	}
        	return selectQueryDatas;
        }else{
        	return null;
        }
	}
	//return column name
	private List<String> handleSumMinMaxAvg(String input){
		List<String> columnNames=new ArrayList<String>();
		if(!CheckSelectQuery(input)){
			return null;
		}
		StringBuilder sb=new StringBuilder(input);
		for(int i=0;i<sb.length();){
			if(sb.charAt(i)=='(' || sb.charAt(i)==')'){
				sb.deleteCharAt(i);
			}else{
				i++;
			}
		}
		columnNames.add(sb.toString());
		return columnNames;
	}
	private void handleAll(List<Row> rows){
		SelectQueryData data=new SelectQueryData();
		data.setSelectQueryType(SelectQueryType.ALL);
		selectQueryDatas.add(data);	
	}
	private boolean handleDistinct(String input,List<Row> rows,List<Field> fields){
		SelectQueryData selectQueryData=new SelectQueryData();
		selectQueryData.setSelectQueryType(SelectQueryType.DISTINCT);
		String regex="distinct";
		List<String> items = Utilities.SplitByRegex(input, regex);
		if (items.size() == 1) {
			String temp = items.get(0);
			if(CheckOpenClose(temp)){//co dau dong mo ngoac
				String returnedResult=Utilities.removeAllOpenClose(temp);
				List<String> columnNames=Utilities.SplitByRegex(returnedResult,"(,)");
				selectQueryData.setColumnNames(columnNames);
				selectQueryDatas.add(selectQueryData);
				return true;
			}else{//khong co dau dong mo ngoac
				List<String> columnNames=new ArrayList<String>();
				columnNames.add(temp);
				selectQueryData.setColumnNames(columnNames);
				selectQueryDatas.add(selectQueryData);
				return true;
			}
		}else{
			return false;
		}
	}
	private List<Integer> GetCommaPosition(String input){
		List<Integer> results=new ArrayList<Integer>();
		boolean isInBlock=false;
		for(int i=0;i<input.length();i++){
			if(input.charAt(i)==',' && isInBlock==false){
				results.add(i);
			}else if(input.charAt(i)=='(' && isInBlock==false){
				isInBlock=true;
			}else if(input.charAt(i)==')' && isInBlock==true){
				isInBlock=false;
			}
		}
		return results;
	}
	private boolean CheckSelectQuery(String input){
		int openCount=0;
		int closeCount=0;
		for(int i=0;i<input.length();i++){
			if(input.charAt(i)=='('){
				openCount++;
			}else if(input.charAt(i)==')'){
				closeCount++;
			}
		}
		if(openCount==closeCount){
			return true;
		}
		return false;
	}
	public static boolean CheckOpenClose(String input){
		int openCount=0;
		int closeCount=0;
		for(int i=0;i<input.length();i++){
			if(input.charAt(i)=='('){
				openCount++;
			}else if(input.charAt(i)==')'){
				closeCount++;
			}
		}
		if(openCount==closeCount && openCount>0 && closeCount>0){
			return true;
		}
		return false;
	}
	private List<String> SplitByPosition(String input,List<Integer> positions){
		List<String> returnedResults=new ArrayList<String>();
		int beginIndex=0;
		for(int i=0;i<positions.size();i++){
			String temp=input.substring(beginIndex,positions.get(i));
			returnedResults.add(temp);
			beginIndex=positions.get(i)+1;
		}
		returnedResults.add(input.substring(beginIndex,input.length()));
		return returnedResults;
	}
}
